--[[
Name: BlastOffBars-1.0
Revision: $Rev$
Author: Astromech AKA Lilki of Dragonmaw-US and Nadine of Detheroc-US
Inspried by: Forte Warlock Cooldown Timer by Xus and CandyBar by Ammo
Website: 
Documentation: 
SVN: 
Description: A compact timer bar library
Dependencies: LibStub, PaintChips-2.0 (optional)
]]

--/script BOB = LibStub('BlastOffBars-1.0'); BOB:RegisterBlastOffBar('test', 'Interface\\Icons\\Temp', 0, 1); BOB:ShowBlastOffBar('test')
--/script BOB:SetBlastOffBarIcon('test', nil)

local MAJOR_VERSION = "BlastOffBars-1.0"
local MINOR_VERSION = tonumber(("$Revision$"):match("%d+")) or 0

local lib, oldMinor = LibStub:NewLibrary(MAJOR_VERSION, MINOR_VERSION)
if not lib then
	return
end

local paint = LibStub:GetLibrary("PaintChips-2.0", true)

if not lib.frame then
	lib.frame = CreateFrame("Frame")
	lib.frame:Hide()
	lib.frame.name = ("%s frame"):format(MAJOR_VERSION)
end

lib.bars = lib.bars or {}
lib.groups = lib.groups or {}
lib.framecache = lib.framecache or {}
lib.embedded = lib.embedded or {}

local release, acquire
do
	local cache = setmetatable({},{__mode='k'})
	function acquire()
		local t = next(cache)
		if t then
			cache[t] = nil
			return t
		else
			return {}
		end
	end
	function release(t)
		if not t then return end
		for k in pairs(t) do
			t[k] = nil
		end
		cache[t] = true
		return nil
	end
end

local defaults = {
	texture = "Interface\\TargetingFrame\\UI-StatusBar",
	--border = "Interface\\Tooltips\\UI-Tooltip-Border",
	--borderinsets = {left = 4, right = 4, top = 4, bottom = 4},
	--bordersize = 16,
	width = 200,
	height = 16,
	scale = 1,
	point = "CENTER",
	rframe = UIParent,
	rpoint = "CENTER",
	xoffset = 0,
	yoffset = 0,
	color = {1, 0, 1, 1},
	--bordercolor = {0, 0, 0, 1},
	textcolor = {1, 1, 1, 1},
	textinterval = 0,
	textfont = "Fonts\\FRITZQT__ttf",
	textsize = 11,
    stayonscreen = false,
	reversed = false, -- meaning right to left
	timertype = "FLAT", -- 0 = Flat, 1 = Logarithmic, 2 = Exponential
	iconside = "LEFT",
}

function lib:SetShow(frame, bool)
	if bool then
		frame:Show()
	else
		frame:Hide()
	end
end

function lib:GetColor(default, redorname, greenoralpha, blue, alpha)
	local t = acquire()
	if type(redorname) == "string" then
		if paint then
			t.r, t.g, t.b = select(2, paint:GetRGBPercent(redorname))
			t.a = greenoralpha or 1
		else
			error(MAJOR_VERSION.." requires PaintChips-2.0 to use color names.")
			t.r, t.g, t.b, t.a = unpack(default)
		end
	elseif type(redorname) == "number" then
		t.r, t.g, t.b = redorname, greenoralpha, blue
		t.a = alpha or 1
	else
		t.r, t.g, t.b, t.a = unpack(default)
	end
	return t
end

function lib:GetStatusBarPos(icon, iconside, width, height)
	local barwidth, baroffset
	if icon then
		barwidth = width - height
		if iconside == 'LEFT' then
			baroffset = height
		else
			baroffset = 0
		end
	else
		barwidth = width
		baroffset = 0
	end
	return barwidth, baroffset
end

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Registering and Unregistering BlastOffBars
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	
function lib:RegisterBlastOffBar(name, icon, min, max, ...)
	if lib.bars[name] then
		lib:UnregisterBlastOffBar(name)
	end
	assert(min < max)
	local bar = acquire()
	lib.bars[name] = bar
	bar.name, bar.icon, bar.timermin, bar.timermax = name, icon, min, max
	bar.color = lib:GetColor(defaults.color, ...)
	bar.visible = false
	bar.frame = lib:AcquireFrame(name)
end

function lib:UnregisterBlastOffBar(name)
	local bar = assert(lib.bars[name])
	if not bar then
		return
	end
	if bar.group
		then lib:UnregisterBlastOffBarFromGroup(name, bar.group)
	end
	release(bar.color)
	release(bar.textcolor)
	--release(bar.bordercolor)
	--release(bar.borderinsets)
	lib:ReleaseFrame(name)
	lib.bars[name] = release(bar)
end

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Set and get bar icon, color, and texture attributes
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

function lib:SetBlastOffBarIcon(name, icon, left, right, top, bottom)
	local bar = assert(lib.bars[name])
	bar.icon = icon
	if not icon then
		bar.frame.icon:Hide()
	else
		left = left or 0.07
		right = right or 0.93
		top = top or 0.07
		bottom = bottom or 0.93
		bar.frame.icon:SetNormalTexture(icon)
		bar.frame.icon:GetNormalTexture():SetTexCoord(left, right, top, bottom)
		lib:SetShow(bar.frame.icon, bar.visible)
	end
	local w, o = lib:GetStatusBarPos(icon, bar.iconside or defaults.iconside, bar.width or defaults.width, bar.height or defaults.height)
	bar.frame.statusbar:SetWidth(w)
	bar.frame.statusbar:ClearAllPoints()
	bar.frame.statusbar:SetPoint('LEFT', bar.frame, 'LEFT', o, 0)
end

function lib:GetBlastOffBarIcon(name)
	local bar = assert(lib.bars[name])
	return bar.icon
end

function lib:SetBlastOffBarIconSide(name, side) -- "LEFT" or "RIGHT"
	local bar = assert(lib.bars[name])
	assert(side == "LEFT" or side == "RIGHT")
	bar.iconside = side
	bar.frame.icon:ClearAllPoints()
	bar.frame.icon:SetPoint(side, bar.frame, side, 0, 0)
	local w, o = lib:GetStatusBarPos(bar.icon or nil, side, bar.width or defaults.width, bar.height or defaults.height)
	bar.frame.statusbar:SetWidth(w)
	bar.frame.statusbar:ClearAllPoints()
	bar.frame.statusbar:SetPoint('LEFT', bar.frame, 'LEFT', o, 0)
end

function lib:GetBlastOffBarIconSide(name)
	local bar = assert(lib.bars[name])
	return bar.iconside or defaults.iconside
end

function lib:SetBlastOffBarColor(name, ...)
	local bar = assert(lib.bars[name])
	local c = lib:GetColor(defaults.color, ...)
	bar.color = c
	bar.frame.statusbar:SetStatusBarColor(c.r, c.g, c.b, c.a)
end

function lib:GetBlastOffBarColor(name)
	local bar = assert(lib.bars[name])
	local c = bar.color
	return c.r, c.g, c.b, c.a
end

function lib:SetBlastOffBarTexture(name, texture)
	local bar = assert(lib.bars[name])
	bar.texture = texture or defaults.texture
	bar.frame.statusbar:SetStatusBarTexture(texture)
end

function lib:GetBlastOffBarTexture(name)
	local bar = assert(lib.bars[name])
	return bar.texture or defaults.texture
end

--[[
function lib:SetBlastOffBarBorderColor(name, ...)
	local bar = assert(lib.bars[name])
	bar.bordercolor = lib:GetColor(defaults.bordercolor, ...)
end

function lib:GetBlastOffBarBorderColor(name)
	local bar = assert(lib.bars[name])
	local c = bar.bordercolor
	return c.r, c.g, c.b, c.a
end
--]]

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Set and get bar timer attributes
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
		
function lib:SetBlastOffBarTimerLimits(name, min, max)
	local bar = assert(lib.bars[name])
	assert(min < max)
	bar.timermin, bar.timermax = min, max
	--lib:Redraw(name)
end

function lib:GetBlastOffBarTimerLimits(name)
	local bar = assert(lib.bars[name])
	return bar.min, bar.max
end

function lib:SetBlastOffBarTimerType(name, type) -- "FLAT", "LOGARITHMIC", "EXPONENTIAL"
	local bar = assert(lib.bars[name])
	assert(type == "FLAT" or type == "LOGARITHMIC" or type == "EXPONENTIAL")
	bar.timertype = type
	--lib:Redraw(name)
end

function lib:GetBlastOffBarTimerType(name)
	local bar = assert(lib.bars[name])
	return bar.timertype or defaults.timertype
end

function lib:SetBlastOffBarReversed(name, reversed)
	local bar = assert(lib.bars[name])
	bar.reversed = reversed
	--Redraw(name)
end

function lib:IsBlastOffBarReversed(name)
	local bar = assert(lib.bars[name])
	return bar.reversed or defaults.reversed
end

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Set and get bar text attributes
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--[[
function lib:SetBlastOffBarTextInterval(name, interval)
	local bar = assert(lib.bars[name])
	assert(interval > 1)
end

function lib:GetBlastOffBarTextInterval(name)
	local bar = assert(lib.bars[name])
	return bar.textinterval or 0
end

function lib:SetBlastOffBarTextFont

function lib:SetBlastOffBarTextSize

function lib:SetBlastOffBarTextColor

function lib:SetBlastOffBarTextFormat
--]]

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Set and get bar position and size attributes
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	
function lib:SetBlastOffBarPoint(name, point, rframe, rpoint, xoffset, yoffset)
	local bar = assert(lib.bars[name])
	bar.point = point or defaults.point
	bar.rframe = rframe or defaults.rframe
	bar.rpoint = rpoint or defaults.rpoint
	bar.xoffset = xoffset or defaults.xoffset
	bar.yoffset = yoffset or defaults.yoffset
	bar.frame:ClearAllPoints()
	bar.frame:SetPoint(bar.point, bar.rframe, bar.rpoint, bar.xoffset, bar.yoffset)
end

function lib:GetBlastOffBarPoint(name)
	local bar = assert(lib.bars[name])
	return bar.point or defaults.point, bar.rframe or defaults.rframe, bar.rpoint or defaults.rpoint, bar.xoffset or defaults.xoffset, bar.yoffset or defaults.yoffset
end

function lib:SetBlastOffBarWidth(name, width) end

function lib:GetBlastOffBarWidth(name) end

function lib:SetBlastOffBarHeight(name, height) end

function lib:GetBlastOffBarHeight(name) end

function lib:SetBlastOffBarScale(name) end

function lib:GetBlastOffBarScale(name) end

function lib:GetBlastOffBarCenter(name) 
	local bar = assert(lib.bars[name])
	return bar.frame:GetCenter()
end

function lib:GetBlastOffBarOffsets(name) 
	local bar = assert(lib.bars[name])
	local frame = bar.frame
	local bottom = frame:GetBottom()
	local top = frame:GetTop()
	local left = frame:GetLeft()
	local right = frame:GetRight()
	return left, top, bottom, right
end

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Functions to create, release, and cache frames
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

function lib:AcquireFrame(name)

	local bar = assert(lib.bars[name])
	local f = bar.frame
	if not f then
		if #lib.framecache > 0 then
			f = table.remove(lib.framecache)
		else
			f = CreateFrame('Button', nil, UIParent)
		end
	end
	f:Hide()
	
	local width = bar.width or defaults.width
	local height = bar.height or defaults.height
	local point = bar.point or defaults.point
	local rframe = bar.rframe or defaults.rframe
	local rpoint = bar.rpoint or defaults.rpoint
	local scale = bar.scale or defaults.scale
	local xoffset = bar.xoffset or defaults.xoffset
	local yoffset = bar.yoffset or defaults.yoffset
	--local border = bar.border or defaults.border
	--local bordercolor = bar.bordercolor or defaults.bordercolor
	--local borderinsets = bar.borderinsets or defaults.borderinsets
	--local bordersize = bar.bordersize or defaults.bordersize
	local texture = bar.texture or defaults.texture
	local color = bar.color or defaults.color	
	local icon = bar.icon or nil
	local iconside = bar.iconside or defaults.iconside
	
	f.owner = name
	f:SetWidth(width)
	f:SetHeight(height)
	f:SetScale(scale)
	f:ClearAllPoints()
	f:SetPoint(point, rframe, rpoint, xoffset, yoffset)
	f:EnableMouse(false)
	f:RegisterForClicks()
	f:SetScript("OnClick", nil)
	--f:SetBackdrop({bgFile = '', edgeFile = border, tile = false, tileSize = 0, edgeSize = bordersize, insets = borderinsets})
	--f:SetBackdropBorderColor(bordercolor.r, bordercolor.g, bordercolor.b, bordercolor.a)
	
	if not f.icon then
		f.icon = CreateFrame('Button', nil, f)
	end
	f.icon.owner = name
	f.icon:SetWidth(height)
	f.icon:SetHeight(height)
	f.icon:EnableMouse(false)
	f.icon:RegisterForClicks()
	f.icon:SetScript("OnClick", nil)
	f.icon:SetNormalTexture(icon)
	if icon then
		f.icon:GetNormalTexture():SetTexCoord(0.07, 0.93, 0.07, 0.93)
	end
	f.icon:SetAlpha(1)
	lib:SetShow(f.icon, icon)
	f.icon:ClearAllPoints()
	f.icon:SetPoint(iconside, f, iconside, 0, 0)
	
	if not f.statusbar then
		f.statusbar = CreateFrame('StatusBar', nil, f)
	end
	f.statusbar.owner = name
	local w, o = lib:GetStatusBarPos(icon, iconside, width, height)
	f.statusbar:SetWidth(w)
	f.statusbar:SetHeight(height)
	f.statusbar:ClearAllPoints()
	f.statusbar:SetPoint('LEFT', f, 'LEFT', o, 0)
	f.statusbar:EnableMouse(false)
	f.statusbar:SetMinMaxValues(0, 1)
	f.statusbar:SetValue(1)
	f.statusbar:SetStatusBarTexture(texture)
	f.statusbar:SetStatusBarColor(color.r, color.g, color.b, color.a)
	--f.statusbar:SetBackdrop({bgFile = texture, edgeFile = border, tile = false, tileSize = 0, edgeSize = bordersize, insets = borderinsets})
	--f.statusbar:SetBackdropBorderColor(bordercolor.r, bordercolor.g, bordercolor.b, bordercolor.a)
	f.statusbar:Show()
	
	lib:SetShow(f, bar.visible)
	return f
end

function lib:ReleaseFrame(name)
	local bar = assert(lib.bars[name])
	bar.visible = false
	bar.frame:Hide()
	table.insert(lib.framecache, bar.frame)
end

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Functions to show and hide individual bar frames
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

function lib:ShowBlastOffBar(name)
	local bar = assert(lib.bars[name])
	bar.visible = true
	bar.frame:Show()
end

function lib:HideBlastOffBar(name)
	local bar = assert(lib.bars[name])
	bar.visible = false
	bar.frame:Hide()
end

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Functions to embed BlastOffBars into an addon and final set ups
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

local embedfuncs = {
	'RegisterBlastOffBar', 'UnregisterBlastOffBar',
	'SetBlastOffBarIcon', 'GetBlastOffBarIcon', 'SetBlastOffBarIconSide', 'GetBlastOffBarIconSide',
	'SetBlastOffBarColor', 'GetBlastOffBarColor',
	'SetBlastOffBarTexture', 'GetBlastOffBarTexture',
	--'SetBlastOffBarBorderColor', 'GetBlastOffBarBorderColor',
	--'SetBlastOffBarBorder', 'GetBlastOffBarBorder',
	'SetBlastOffBarTimerLimits', 'GetBlastOffBarTimerLimits', 'SetBlastOffBarTimerType', 'GetBlastOffBarTimerType',
	'SetBlastOffBarReversed', 'IsBlastOffBarReversed',
	'SetBlastOffBarPoint', 'GetBlastOffBarPoint', 'GetBlastOffBarCenter', 'GetBlastOffBarOffsets',
	'SetBlastOffBarWidth', 'GetBlastOffBarWidth', 'SetBlastOffBarHeight', 'GetBlastOffBarHeight',
	'SetBlastOffBarScale', 'GetBlastOffBarScale',
	'ShowBlastOffBar', 'HideBlastOffBar',
}	
function lib:Embed(target)
	for _,v in pairs(embedfuncs) do
		target[v] = lib[v]
	end
	lib.embedded[target] = true
end

for k in pairs(lib.embedded) do
	lib:Embed(k)
end

lib.frame:SetScript('OnUpdate', OnUpdate)